﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Data.GameData.Utils;

namespace Data.GameData
{
    public static class SystemGeneration
    {
        /// <summary>
        /// Randomly pick a star type.
        /// </summary>
        /// <returns>The type of a star.</returns>
        public static StarType RandomStarType()
        {
            int roll = UniqueRandom.Next(1, 100);

            if (roll <= 5)
                return StarType.BLUE_GIANT;
            if (roll <= 16)
                return StarType.WHITE_STAR;
            if (roll <= 41)
                return StarType.YELLOW_STAR;
            if (roll <= 66)
                return StarType.ORANGE_STAR;
            if (roll <= 85)
                return StarType.RED_STAR;
            if (roll <= 95)
                return StarType.RED_DWARF;
            if (roll <= 98)
                return StarType.WHITE_DWARF;

            return StarType.RED_GIANT;
        }

        /// <summary>
        /// Calculate the LM distances for the system bodies in a star system.
        /// </summary>
        /// <returns>The list of LM orbits</returns>
        public static List<int> CalculatePlanetaryOrbits()
        {
            List<int> orbits = new List<int>();

            int a = UniqueRandom.Next(1, 8);
            int b = UniqueRandom.Next(a + 2, 10);

            orbits.Add(a);
            orbits.Add(b);

            int x = 1;
            double z = Math.Pow(2, x);
            int o = a + ((b - a) * (int)Math.Pow(2, x));

            while (o < 300)
            {
                orbits.Add(o);
                ++x;
                o = a + ((b - a) * (int)Math.Pow(2, x));
            }

            return orbits;
        }

        /// <summary>
        /// Calculate the size and type of planet.
        /// </summary>
        /// <param name="starType">The type of star the planet orbits</param>
        /// <param name="lmDist">The distance in LM from the star</param>
        /// <param name="planet">The planet to modify</param>
        public static void DertermineEnviroment(StarType starType, int lmDist, ref SystemBody planet)
        {
            int maxRockyLm = 0;
            int maxGasLm = 0;
            int minBioLm = 0;
            int maxBioLm = 0;
            //int maxTidelockLm = 0;

            switch (starType)
            {
                case StarType.WHITE_STAR:
                    {
                        maxRockyLm = 25;
                        maxGasLm = 130;
                        minBioLm = 10;
                        maxBioLm = 18;
                        //maxTidelockLm = 5;
                    }
                    break;
                case StarType.YELLOW_STAR:
                    {
                        maxRockyLm = 16;
                        maxGasLm = 83;
                        minBioLm = 6;
                        maxBioLm = 12;
                        //maxTidelockLm = 3;
                    }
                    break;
                case StarType.ORANGE_STAR:
                    {
                        maxRockyLm = 9;
                        maxGasLm = 38;
                        minBioLm = 3;
                        maxBioLm = 5;
                        //maxTidelockLm = 2;
                    }
                    break;
                case StarType.RED_STAR:
                    {
                        maxRockyLm = 5;
                        maxGasLm = 18;
                        minBioLm = 3;
                        maxBioLm = 4;
                        //maxTidelockLm = 2;
                    }
                    break;
                case StarType.RED_DWARF:
                    {
                        maxRockyLm = 3;
                        maxGasLm = 11;
                        minBioLm = 0;
                        maxBioLm = 0;
                        //maxTidelockLm = 1;
                    }
                    break;
                default:
                    throw new NotSupportedException("Star cannot support planets.");
            }

            int roll = UniqueRandom.Next(1, 100);
            if (roll <= 25)
            {
                planet.Mass = 1;
                if (lmDist <= maxRockyLm)
                {
                    if( lmDist < minBioLm)
                        planet.Enviroment = SystemBodyEnviroment.TYPE_O3;
                    if (lmDist >= minBioLm)
                        planet.Enviroment = SystemBodyEnviroment.TYPE_O2;

                    return;
                }
                if (lmDist <= maxGasLm)
                {
                    planet.Enviroment = SystemBodyEnviroment.TYPE_G;
                    return;
                }

                planet.Enviroment = SystemBodyEnviroment.TYPE_I;
                return;
            }
            if (roll <= 75)
            {
                planet.Mass = 2;
                if (lmDist <= maxRockyLm)
                {
                    if (lmDist < minBioLm)
                        planet.Enviroment = SystemBodyEnviroment.TYPE_V;
                    if (lmDist >= minBioLm && lmDist <= maxBioLm)
                        planet.Enviroment = SystemBodyEnviroment.TYPE_T;
                    if (lmDist > maxBioLm)
                        planet.Enviroment = SystemBodyEnviroment.TYPE_O2;

                    return;
                }
                if (lmDist <= maxGasLm)
                {
                    planet.Enviroment = SystemBodyEnviroment.TYPE_G;
                    return;
                }

                planet.Enviroment = SystemBodyEnviroment.TYPE_I;
                return;
            }

            planet.Mass = 3;
            if (lmDist <= maxRockyLm)
            {
                if (lmDist < minBioLm)
                    planet.Enviroment = SystemBodyEnviroment.TYPE_V;
                if (lmDist >= minBioLm && lmDist <= maxBioLm)
                    planet.Enviroment = SystemBodyEnviroment.TYPE_ST;
                if (lmDist > maxBioLm)
                    planet.Enviroment = SystemBodyEnviroment.TYPE_O2;

                return;
            }
            if (lmDist <= maxGasLm)
            {
                planet.Enviroment = SystemBodyEnviroment.TYPE_G;
                return;
            }

            planet.Enviroment = SystemBodyEnviroment.TYPE_I;
            return;
        }

        /// <summary>
        /// Determine the number of moons a planet should have based on the planets mass and type.
        /// </summary>
        /// <param name="mass">The planets mass</param>
        /// <param name="environment">The planets type</param>
        /// <returns>The number of moons the planet has</returns>
        public static int MoonCount(int mass, SystemBodyEnviroment environment)
        {
            int moons = 0;

            int modifier = 0;
            if (environment != SystemBodyEnviroment.TYPE_G && environment != SystemBodyEnviroment.TYPE_I)
            {
                if (mass == 1)
                    modifier += -50;
                if (mass == 2)
                    modifier += -10;
            }
            if( environment == SystemBodyEnviroment.TYPE_O3)
                modifier += -15;
            if (environment == SystemBodyEnviroment.TYPE_V)
                modifier += -35;
            if (environment == SystemBodyEnviroment.TYPE_I)
                modifier += 35;
            if (environment == SystemBodyEnviroment.TYPE_G)
                modifier += 50;

            int roll = UniqueRandom.Next(1, 100) + modifier;
            if (roll <= 1)
                moons = 0;
            else if (roll <= 55)
                moons = 1;
            else if (roll <= 85)
                moons = 2;
            else if (roll <= 105)
                moons = 3;
            else if (roll <= 126)
                moons = 4;
            else
                moons = 5;

            return moons;
        }
    }
}
